package cn.shop.crontab.service;

import cn.hutool.core.util.NumberUtil;
import cn.shop.crontab.mapper.FrontUserMapper;
import cn.shop.transcation.bankcard.entity.BankCard;
import cn.shop.transcation.detail.entity.FundDetail;
import cn.shop.transcation.detail.mapper.FundDetailMapper;
import cn.shop.transcation.frontUserModule.FrontUser;
import cn.shop.transcation.withdraw.entity.Withdraw;
import cn.shop.transcation.withdraw.mapper.WithdrawMapper;
import cn.shop.utils.enums.ProcessState;
import cn.shop.utils.enums.WithdrawAccountType;
import cn.shop.utils.enums.WithdrawChannel;
import cn.shop.utils.exception.ServiceException;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class WithdrawService {

	@Resource
	private FrontUserMapper frontUserService;

	@Resource
	private FundDetailMapper fundDetailMapper;
	@Resource
	private WithdrawMapper withdrawMapper;
	@Resource
	private RedissonClient redissonClient;

	@Transactional(rollbackFor = Exception.class)
	public void withdraw(FrontUser user, BankCard card, BigDecimal amount, String v, String v2) {
		RLock lock = redissonClient.getLock("withdraw-" + user.getId());
		try {
			if (lock.tryLock(0, 60, TimeUnit.SECONDS)) {
					doWithdraw(user, card, amount, v, v2);
			} else {
				throw new ServiceException("有正在进行的提现，请1分钟后再试");
			}
		} catch (Exception e) {
			log.warn("提现并发异常", e);
			throw new ServiceException("提现失败，请重试");
		} finally {
			if (lock.isLocked() && lock.isHeldByCurrentThread()) {
				lock.unlock();
			}
		}
	}

	private void doWithdraw(FrontUser user, BankCard card, BigDecimal amount, String v, String v2) {
		log.info("用户 {} 开始进行自动提现", user.getId());

		Withdraw withdraw = new Withdraw();
		withdraw.setUserId(user.getId());
		withdraw.setAmount(amount);
		withdraw.setBalance(user.getBalance().subtract(amount));
		withdraw.setState(ProcessState.UNTREATED);
		withdraw.setType(2);
		withdraw.setChannel(WithdrawChannel.AUTO);
		BigDecimal charges;
		//rate 百分比提成， single 单笔扣款
		BigDecimal rate = BigDecimal.ZERO, single = BigDecimal.ZERO;

		if (NumberUtil.isNumber(v)) {
			charges = new BigDecimal(v).divide(new BigDecimal(100), 4, RoundingMode.CEILING);
			if (charges.compareTo(BigDecimal.ZERO) > 0) {
				rate = amount.multiply(charges);
			}
		}
		//计算单笔扣款

		if (NumberUtil.isNumber(v)) {
			charges = new BigDecimal(v2);
			if (charges.compareTo(BigDecimal.ZERO) > 0) {
				single = charges;
			}
		}
		withdraw.setActualAmount(amount.subtract(rate).subtract(single));
		withdraw.setCharges(rate.add(single));
		withdraw.setAccount(card.getCardNo());
		withdraw.setAccountType(WithdrawAccountType.BANK_PAYMENT);
		withdraw.setBankName(card.getBankName());
		withdraw.setBankAccountName(card.getName());
		withdraw.setChargeRate(rate);
		withdraw.setChargeSingle(single);
		withdraw.setIdCard(user.getIdentityCard());
		withdraw.setPhone(user.getPhone());
		int success = withdrawMapper.insert(withdraw);
		if (success <= 0) {
			log.warn("自动提现保存数据失败，{}", withdraw);
			throw new RuntimeException("提现失败, 提现记录保存失败");
		}
		FrontUser update = new FrontUser();
		update.setId(user.getId());
		LambdaUpdateWrapper<FrontUser> updates = Wrappers.lambdaUpdate(update)
				.setSql("balance = balance - " + amount)
				.eq(FrontUser::getId, user.getId());
		success = frontUserService.update(update, updates);
		if (success <= 0) {
			log.warn("修改用户可提现金额失败, 用户号：{}， 时间：{}", user.getId(), LocalDateTime.now());
			throw new RuntimeException("提现失败，用户余额修改失败");
		}
		FundDetail detail = new FundDetail();
		detail.setType(3);
		detail.setUid(0L);
		detail.setOrderId(withdraw.getId());
		detail.setPlayerId(user.getId());
		detail.setPlayerAccount(user.getAccount());
		detail.setPlatformShare(withdraw.getCharges());
		detail.setBalance(user.getBalance().subtract(amount));
		detail.setAmount(amount);
		detail.setAlias("www");
		detail.setIp("0.0.0.0");
		detail.setGoods("玩家提现");
		detail.setDealTime(LocalDateTime.now());
		success = fundDetailMapper.insert(detail);
		if (success <= 0) {
			log.warn("交易记录保存失败");
			throw new RuntimeException("提现失败，交易记录保存失败");
		}
	}
}
